package com.dtp.extension.limiter.redis.ratelimiter;

import com.dtp.common.pattern.filter.Invoker;
import com.dtp.core.context.BaseNotifyCtx;
import com.dtp.core.notify.filter.NotifyFilter;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.collections.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;

/**
 * 通过redis限流器实现通知过滤
 *
 * @author yanhom
 * @since 1.0.8
 *
 * @remark muse
 **/
@Slf4j
public class NotifyRedisRateLimiterFilter implements NotifyFilter {

    public static final int LUA_RES_REMAIN_INDEX = 2; // 获取返回值res中下标为2的信息，即：remain（还可以添加的元素个数）

    @Resource
    private RedisRateLimiter<List<Long>> redisScriptRateLimiter;

    /** 执行过滤操作 */
    @Override
    public void doFilter(BaseNotifyCtx context, Invoker<BaseNotifyCtx> nextFilter) {
        // 构建redis中缓存的zset的名称key，由[threadPoolName]+":"+[NotifyItemEnum.value]组成
        String notifyName = context.getExecutorWrapper().getThreadPoolName() + ":" + context.getNotifyItemEnum().getValue();

        // 校验是否被限流
        boolean checkResult = check(notifyName, context.getNotifyItem().getClusterLimit(), context.getNotifyItem().getInterval());

        // 如果没有被限流，则继续将操作权利向后传递
        if (checkResult) nextFilter.invoke(context);
    }

    /** 判断没有被限流 true：没有被限流  false：被限流了*/
    private boolean check(String notifyName, int limit, long interval) {
        val res = redisScriptRateLimiter.isAllowed(notifyName, interval, limit);
        if (CollectionUtils.isEmpty(res)) return true;

        if (res.get(LUA_RES_REMAIN_INDEX) <= 0) { // res=[返回集合的主键值(key), 过期剩余时间(ttl), 还可以添加的元素个数(remain)]
            log.debug("DynamicTp notify trigger rate limit, limitKey:{}", res.get(0));
            return false;
        }
        return true;
    }

    @Override
    public int getOrder() {return 10;}
}
